package com.ruoyi.project.blog.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.FieldConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.RedisConstants;
import com.ruoyi.common.enums.ReadTypeEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.IpUtil;
import com.ruoyi.common.utils.bean.RelativeDateFormat;
import com.ruoyi.common.utils.bean.ResponseResult;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.blog.domain.*;
import com.ruoyi.project.blog.domain.dto.ArticleDTO;
import com.ruoyi.project.blog.domain.vo.*;
import com.ruoyi.project.blog.mapper.*;
import com.ruoyi.project.blog.service.IBArticleService;
import com.ruoyi.project.blog.service.IBRedisService;
import com.ruoyi.project.tool.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.common.constant.Constants.IMG_URL_API;
import static com.ruoyi.common.constant.RedisConstants.*;

/**
 * 博客文章Service业务层处理
 *
 * @author ruoyi
 * @date 2023-09-21
 */
@Service
public class BArticleServiceImpl extends ServiceImpl<BArticleMapper, BArticle> implements IBArticleService {
    @Autowired
    private BArticleMapper bArticleMapper;
    @Autowired
    private BCategoryMapper categoryMapper;
    @Autowired
    private BTagsMapper tagsMapper;
    @Autowired
    private IBRedisService redisService;
    @Autowired
    private BCollectMapper bCollectMapper;
    @Autowired
    private BCommentMapper bCommentMapper;
    @Autowired
    private BFollowedMapper followedMapper;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private BUserMapper userMapper;


    /**
     * 查询博客文章
     *
     * @param id 博客文章主键
     * @return 博客文章
     */
    @Override
    public BArticle selectBArticleById(Long id) {
        return bArticleMapper.selectBArticleById(id);
    }

    /**
     * 查询博客文章列表
     *
     * @param
     * @return 博客文章
     */
    @Override
    public List<SystemArticleListVO> selectBArticleList(BArticle bArticle) {
        List<SystemArticleListVO> resultList = bArticleMapper.selectArticle(bArticle);
        resultList.forEach(item -> {
            BUser userInfo = userMapper.selectUserById(Long.parseLong(item.getUserId()));
            item.setNickname(userInfo.getNickName());
        });
        return resultList;
    }

    /**
     * 新增博客文章
     *
     * @param bArticle 博客文章
     * @return 结果
     */
    @Override
    public AjaxResult insertBArticle(ArticleDTO bArticle) {
        bArticle.setCreateTime(DateUtils.getNowDate());
        BArticle blogArticle = BeanCopyUtils.copyObject(bArticle, BArticle.class);
        //添加分类
        Long categoryId = savaCategory(bArticle.getCategoryName());
        //添加标签
        List<Long> tagList = getTagsList(bArticle);

        blogArticle.setCategoryId(categoryId);

        int insert = baseMapper.insert(blogArticle);
        if (insert > 0) {
            tagsMapper.saveArticleTags(blogArticle.getId(), tagList);
        }

        return AjaxResult.success();
    }

    /**
     * 修改博客文章
     *
     * @param bArticle 博客文章
     * @return 结果
     */
    @Override
    public int updateBArticle(BArticle bArticle) {
        bArticle.setUpdateTime(DateUtils.getNowDate());
        return bArticleMapper.updateBArticle(bArticle);
    }

    /**
     * 批量删除博客文章
     *
     * @param ids 需要删除的博客文章主键
     * @return 结果
     */
    @Override
    public AjaxResult deleteBArticleByIds(Long[] ids) {
        bArticleMapper.deleteBArticleByIds(ids);
        tagsMapper.deleteByArticleIds(ids);
        return AjaxResult.success();
    }

    /**
     * 删除博客文章信息
     *
     * @param id 博客文章主键
     * @return 结果
     */
    @Override
    public int deleteBArticleById(Long id) {
        return bArticleMapper.deleteBArticleById(id);
    }

    @Override
    public AjaxResult randomImg() {
        //文章封面图片 由https://api.btstu.cn/该网站随机获取
        String result = HttpUtils.sendGet(IMG_URL_API);
        Object imgUrl = JSON.parseObject(result).get("imgurl");
        return AjaxResult.success(imgUrl);
    }

    @Override
    public ArticleDTO selectArticleById(Long id) {
        ArticleDTO articleDTO = baseMapper.selectArticlePrimaryKey(id);
        articleDTO.setTags(tagsMapper.selectByArticleId(id));
        return articleDTO;
    }

    @Override
    public AjaxResult psArticle(ArticleDTO article) {
        baseMapper.publishAndShelf(article);
        return AjaxResult.success();
    }

    @Override
    public List<ApiArticleListVO> selectArticleList(Long categoryId, Long tagId, String orderByDescColumn, Long userId) {
        List<ApiArticleListVO> articlePage = bArticleMapper.selectPublicArticleList(categoryId, tagId, orderByDescColumn, userId);
        articlePage.forEach(item -> {
            setCommentAndLike(item);
            //获取文章
            long collectCount = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getArticleId, item.getId()));
            item.setCollectCount(collectCount);
            //todo 判断当前登录用户是否收藏该文章 标记为收藏
            String principal = SecurityUtils.getAuthentication().getPrincipal().toString();
            Long userKey = "anonymousUser".equals(principal) ? null : SecurityUtils.getUserId();
            if (userKey != null) {
                collectCount = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getArticleId, item.getId())
                        .eq(BCollect::getUserId, SecurityUtils.getUserId()));
                item.setIsCollect(collectCount > 0);
            }
            //格式化时间为几秒前 几分钟前等
            item.setFormatCreateTime(RelativeDateFormat.format(item.getCreateTime()));
        });
        return articlePage;
    }

    @Override
    public ResponseResult selectArticleInfo(Long id) {


        ApiArticleInfoVO apiArticleInfoVO = bArticleMapper.selectArticleByIdToVO(id);
        //设置阅读+1
        int num = apiArticleInfoVO.getQuantity() + 1;
        bArticleMapper.updateQuantityById(id, num);
        //获取收藏量
        Long collectCount = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getArticleId, id));
        apiArticleInfoVO.setCollectCount(Math.toIntExact(collectCount));
        //获取标签
        List<BTags> list = tagsMapper.selectTagByArticleId(apiArticleInfoVO.getId());
        apiArticleInfoVO.setTagList(list);
        //获取评论数量
        List<BComment> comments = bCommentMapper.selectList(
                new LambdaQueryWrapper<BComment>().eq(BComment::getArticleId, id));
        apiArticleInfoVO.setCommentCount(comments.size());
        //获取点赞数量
        Map<String, Object> map = redisService.getCacheMap(ARTICLE_LIKE_COUNT);
        if (map != null && map.size() > 0) {
            apiArticleInfoVO.setLikeCount(map.get(id.toString()));
        }
        //TODO 获取当前登录用户是否点赞该文章
        String principal = SecurityUtils.getAuthentication().getPrincipal().toString();
        Long userId = "anonymousUser".equals(principal) ? null : SecurityUtils.getUserId();
        if (userId != null) {
            String articleLikeKey = ARTICLE_USER_LIKE + userId;
            if (redisService.sIsMember(articleLikeKey, id)) {
                apiArticleInfoVO.setIsLike(true);
                //校验文章用户是否已经点赞过
                if (apiArticleInfoVO.getReadType() == ReadTypeEnum.LIKE.index) {
                    apiArticleInfoVO.setActiveReadType(true);
                }
            }
            //校验文章用户是否已经评论过
            if (apiArticleInfoVO.getReadType() == ReadTypeEnum.COMMENT.index) {
                Long count = bCommentMapper.selectCount(new LambdaQueryWrapper<BComment>().eq(BComment::getUserId, userId));
                if (count != null && count > 0) {
                    apiArticleInfoVO.setActiveReadType(true);
                }
            }

            //校验用户是否收藏文章
            Long collect = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getUserId, userId).eq(BCollect::getArticleId, id));
            apiArticleInfoVO.setIsCollect(Math.toIntExact(collect));

            //校验用户是否关注该文章作者
            Long followed = followedMapper.selectCount(new LambdaQueryWrapper<BFollowed>().eq(BFollowed::getUserId, userId)
                    .eq(BFollowed::getFollowedUserId, apiArticleInfoVO.getUserId()));
            apiArticleInfoVO.setIsFollowed(Math.toIntExact(followed));
        }

        //校验文章是否已经进行过扫码验证
        if (apiArticleInfoVO.getReadType() == ReadTypeEnum.CODE.index) {
            List<Object> cacheList = redisService.getCacheList(RedisConstants.CHECK_CODE_IP);
            String ip = IpUtil.getIp(request);
            if (cacheList.contains(ip)) {
                apiArticleInfoVO.setActiveReadType(true);
            }
        }

        //增加文章阅读量
        redisService.incrArticle(id.longValue(), ARTICLE_READING, IpUtil.getIp(request));
        return ResponseResult.success(apiArticleInfoVO);
    }

    @Override
    public ResponseResult selectUserInfoByArticleId(Long id) {
        BArticle article = bArticleMapper.selectById(id);
        UserInfoVO userInfoVO = userMapper.selectInfoByUserId(article.getUserId());
        return ResponseResult.success(userInfoVO);
    }

    @Override
    public ResponseResult articleLike(Long articleId) {
        // 判断是否点赞
        String articleLikeKey = ARTICLE_USER_LIKE + SecurityUtils.getUserId().toString();
        if (redisService.sIsMember(articleLikeKey, articleId)) {
            // 点过赞则删除文章id
            redisService.sRemove(articleLikeKey, articleId);
            // 文章点赞量-1
            redisService.hDecr(ARTICLE_LIKE_COUNT, articleId.toString(), 1L);
        } else {
            // 未点赞则增加文章id
            redisService.sAdd(articleLikeKey, articleId);
            // 文章点赞量+1
            redisService.hIncr(ARTICLE_LIKE_COUNT, articleId.toString(), 1L);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult archive(Long userId) {
        List<ApiArchiveVO> articleList = bArticleMapper.selectListArchive(userId);
        //按日期分组
        Map<String, List<ApiArchiveVO>> resultList = articleList.stream().collect(Collectors.groupingBy(ApiArchiveVO::getTime));
        Object[] keyArr = resultList.keySet().toArray();  //获取resultList的所有key值数组
        Arrays.sort(keyArr);
        List<Map<String, Object>> result = new ArrayList<>();
        for (int i = keyArr.length - 1; i >= 0; i--) {
            Map<String, Object> map = new HashMap<>();
            map.put("time", keyArr[i]);
            List<ApiArchiveVO> list = resultList.get(keyArr[i]);
            list.sort((o1, o2) -> o2.getFormatTime().compareTo(o1.getFormatTime()));
            map.put("list", list);
            result.add(map);
        }
        return ResponseResult.success(result).putExtra("total", articleList.size());
    }

    @Override
    public List<ApiArticleListVO> searchArticle(String keywords) {
        List<ApiArticleListVO> articlePage = bArticleMapper.selectSearchArticle(keywords);
        articlePage.forEach(item -> {
            item.setTitle(item.getTitle().replaceAll("(?i)" + keywords, Constants.PRE_TAG + keywords + Constants.POST_TAG));
            setCommentAndLike(item);
            //获取收藏文章
            Long collectCount = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getArticleId, item.getId()));
            item.setCollectCount(collectCount);
            //TODO 游客参照这个改
            String principal = SecurityUtils.getAuthentication().getPrincipal().toString();
            Long userId = "anonymousUser".equals(principal) ? null : SecurityUtils.getUserId();
            //判断当前登录用户是否收藏该文章 标记为收藏
            if (userId != null) {
                collectCount = bCollectMapper.selectCount(new LambdaQueryWrapper<BCollect>().eq(BCollect::getArticleId, item.getId())
                        .eq(BCollect::getUserId, SecurityUtils.getUserId().toString()));
                item.setIsCollect(collectCount > 0);
            }
            //格式化时间为几秒前 几分钟前等
            item.setFormatCreateTime(RelativeDateFormat.format(item.getCreateTime()));
        });

        return articlePage;
    }

    /**
     * 设置评论量和点赞量
     *
     * @param item
     */
    private void setCommentAndLike(ApiArticleListVO item) {
        List<BTags> list = tagsMapper.selectTagByArticleId(item.getId());
        Long commentCount = bCommentMapper.selectCount(new LambdaQueryWrapper<BComment>()
                .eq(BComment::getArticleId, item.getId()));
        //获取点赞数量
        Map<String, Object> map = redisService.getCacheMap(ARTICLE_LIKE_COUNT);
        if (map != null && map.size() > 0) {
            Object obj = map.get(item.getId().toString());
            item.setLikeCount(obj == null ? 0 : obj);
        }
        item.setTagList(list);
        item.setCommentCount(Math.toIntExact(commentCount));
    }

    /**
     * 如果分类不存在则新增
     *
     * @param categoryName
     * @return
     */
    private Long savaCategory(String categoryName) {
        BCategory category = categoryMapper.selectOne(new QueryWrapper<BCategory>().eq(FieldConstants.NAME, categoryName));
        if (category == null) {
            category.setName(categoryName);
            category.setCreateTime(DateUtils.getNowDate());
            category.setSort(0L);
            categoryMapper.insert(category);
        }
        return category.getId();
    }

    /**
     * 将数据库不存在的标签新增
     *
     * @param article
     * @return
     */
    private List<Long> getTagsList(ArticleDTO article) {
        List<Long> tagList = new ArrayList<>();
        article.getTags().forEach(item -> {
            BTags tags = tagsMapper.selectOne(new QueryWrapper<BTags>().eq(FieldConstants.NAME, item));
            if (tags == null) {
                tags = new BTags();
                tags.setSort(0L);
                tags.setCreateTime(DateUtils.getNowDate());
                tags.setName(item);
                tagsMapper.insert(tags);
            }
            tagList.add(tags.getId());
        });
        return tagList;
    }

}
